Skip to main content

React Router Setup

The Iquea frontend uses React Router v7 for client-side navigation, providing a seamless single-page application experience.

Router Configuration

The main routing setup is defined in App.tsx:
src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './context/AuthContext';
import { CartProvider } from './context/CartContext';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
import Home from './pages/Home';
import Login from './pages/Login';
import Register from './pages/Register';
import ProductList from './pages/ProductList';
import ProductDetail from './pages/ProductDetail';
import Cart from './pages/Cart';
import Habitaciones from './pages/Habitaciones';
import Nosotros from './pages/Nosotros';
import Contacto from './pages/Contacto';
import './App.css';

function App() {
  return (
    <AuthProvider>
      <CartProvider>
        <BrowserRouter>
          <div className="app-container">
            <Navbar />
            <main className="main-content">
              <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/login" element={<Login />} />
                <Route path="/register" element={<Register />} />
                <Route path="/productos" element={<ProductList />} />
                <Route path="/productos/:id" element={<ProductDetail />} />
                <Route path="/carrito" element={<Cart />} />
                <Route path="/habitaciones" element={<Habitaciones />} />
                <Route path="/nosotros" element={<Nosotros />} />
                <Route path="/contacto" element={<Contacto />} />
              </Routes>
            </main>
            <Footer />
          </div>
        </BrowserRouter>
      </CartProvider>
    </AuthProvider>
  );
}

export default App;

Route Structure

The application defines 9 main routes:

Home

/ - Landing page with featured products

Login

/login - User authentication page

Register

/register - New user registration

Product List

/productos - Browse all products

Product Detail

/productos/:id - Individual product view

Cart

/carrito - Shopping cart (auth required)

Habitaciones

/habitaciones - Room inspiration page

Nosotros

/nosotros - About us page

Contacto

/contacto - Contact information

Route Details

Public Routes

These routes are accessible to all users:
PathComponentDescription
/HomeLanding page with hero and featured products
/loginLoginAuthentication form
/registerRegisterUser registration form
/productosProductListBrowse products with search and filters
/productos/:idProductDetailProduct details with add-to-cart
/habitacionesHabitacionesRoom-based product inspiration
/nosotrosNosotrosCompany information
/contactoContactoContact form and information

Protected Routes

Currently, the cart is the only protected route:
PathComponentAuth Required
/carritoCartYes (soft)
Soft Protection: The cart route is accessible but the Navbar only shows the cart icon when isAuthenticated is true. There’s no explicit route guard in the current implementation.

Dynamic Routes

Product Detail Route

The product detail route uses a URL parameter:
<Route path="/productos/:id" element={<ProductDetail />} />

Accessing Route Parameters

src/pages/ProductDetail.tsx
import { useParams } from 'react-router-dom';
import { getProducto } from '../api/productos';

function ProductDetail() {
  const { id } = useParams<{ id: string }>();
  const [producto, setProducto] = useState<Producto | null>(null);

  useEffect(() => {
    if (id) {
      getProducto(Number(id)).then(setProducto);
    }
  }, [id]);

  // Render product details...
}

Search Query Parameters

The product list supports search via query parameters:
src/components/Navbar.tsx
import { useNavigate } from 'react-router-dom';

function Navbar() {
  const navigate = useNavigate();
  const [searchQuery, setSearchQuery] = useState('');

  function handleSearch(e: React.FormEvent) {
    e.preventDefault();
    if (searchQuery.trim()) {
      // Navigate with query parameter
      navigate(`/productos?q=${encodeURIComponent(searchQuery.trim())}`);
      setSearchQuery('');
    }
  }
  // ...
}
The application uses two main navigation methods:

Declarative Navigation

Using the Link component for standard navigation:
import { Link } from 'react-router-dom';

<Link to="/productos" className="navbar__link">
  Productos
</Link>

<Link to={`/productos/${producto.producto_id}`}>
  View Product
</Link>

Programmatic Navigation

Using the useNavigate hook for navigation after actions:
import { useNavigate } from 'react-router-dom';

function Navbar() {
  const navigate = useNavigate();
  const { logout } = useAuth();

  function handleLogout() {
    logout();
    navigate('/');  // Redirect to home after logout
  }
}

Layout Structure

The routing hierarchy wraps all routes with context providers and shared layout:
AuthProvider
└── CartProvider
    └── BrowserRouter
        └── App Container
            ├── Navbar (persistent)
            ├── <Routes>
            │   └── [Dynamic page content]
            └── Footer (persistent)
This ensures:
  • Persistent UI - Navbar and Footer render on all pages
  • Context availability - Auth and Cart state available to all routes
  • Smooth transitions - Only the main content area changes between routes

Route Protection (Future Enhancement)

While the current implementation doesn’t have explicit route guards, here’s how you could add them:
src/components/ProtectedRoute.tsx
import { Navigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';

interface Props {
  children: React.ReactNode;
}

export default function ProtectedRoute({ children }: Props) {
  const { isAuthenticated } = useAuth();

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}

Authentication Flow

The authentication flow uses programmatic navigation:
  1. Login Success → Navigate to home or intended page
  2. Logout → Navigate to home
  3. Unauthorized Access → Navigate to login (if implementing route guards)
src/pages/Login.tsx
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import { login } from '../api/auth';

function Login() {
  const navigate = useNavigate();
  const { setToken } = useAuth();

  async function handleSubmit(credentials: LoginDTO) {
    try {
      const { token } = await login(credentials);
      setToken(token);
      navigate('/');  // Redirect after successful login
    } catch (error) {
      // Handle error
    }
  }
}

Best Practices

Always validate and convert URL parameters:
const { id } = useParams<{ id: string }>();
const productId = Number(id);

if (isNaN(productId)) {
  // Handle invalid ID
  return <NotFound />;
}
Always encode query parameters:
const encodedQuery = encodeURIComponent(searchQuery.trim());
navigate(`/productos?q=${encodedQuery}`);

Next Steps

State Management

Learn how AuthContext and CartContext work

API Integration

See how pages fetch data from the backend